"""RFB protocol initialization states."""

from . import (
    coreStates,
    encodings,
    exceptions,
    security,
)

from .baseStates import (
    BaseState,
    DataExpectState,
    DynamicDataState,
    ReceiveStringState,
    StructExpectState,
)

class GetServerVersion(DataExpectState):
    """First thing to do -- to receive servers' version."""

    dataSize = len("RFB xxx.yyy\n")

    def act(self, client):
        _versionString = client.recvData(self.dataSize)
        (_prefix, _version) = _versionString.strip().split()
        if _prefix != "RFB":
            raise Exception(
                "Failed to parse version string %r" % _versionString)
        _versionTuple = tuple(map(int, _version.split(".")))
        client.log("Server is using protocol %i.%i" % _versionTuple)
        if client.protocolVersion > _versionTuple:
            raise exceptions.ProtocolError(
                "Fallback to server protocol "\
                "version %i.%i not supported." % _versionTuple)
        # else assuming that server is capable of fallback
        # to our version
        client.sendData("RFB %03i.%03i\n" % client.protocolVersion)
        return NegotiateSecurity()

class ProtocolFailure(ReceiveStringState):

    def act(self, client):
        _reason = client.recvData(self.getDataSize(client))
        raise exceptions.ServerError(_reason)

class NegotiateSecurity(DynamicDataState):

    countFieldType = "U8"

    # data fields have "U8", type and, hence, are one byte in length
    # no need to overlay `getDataSize`

    def getSupportedSecurity(self):
        """Return supported security classes.

        (Ordered by preference (more preferrable first)).
        """
        _supported = [_el for _el in security.authTypes if _el.supported]
        _supported.sort(key=_el.priority, reverse=True)
        return _supported

    def act(self, client):
        if self.dataCount == 0:
            # as stated in documentation
            return ProtocolFailure()
        _srvSecStr = client.recvData(self.getDataSize(client))
        _secStruct = ("U8", ) * self.dataCount
        _serverSupportedTypes = client.types.unpack(_secStruct, _srvSecStr)
        _srvSecurity = set()
        for _id in _serverSupportedTypes:
            _obj = [_el for _el in security.authTypes if _el.id == _id]
            if not _obj:
                client.log("Got totally unknown security type %i" % _id)
                continue
            _srvSecurity.add(_obj[0])
        client.log("Server supports securities: " + ", ".join(
            _el.name for _el in _srvSecurity))
        _clientSecurity = self.getSupportedSecurity()
        client.log("Client supports securities: " + ", ".join(
            _el.name for _el in _clientSecurity))
        _goodSecs = [_el for _el in _clientSecurity if _el in _srvSecurity]
        if not _goodSecs:
            raise SecurityError("Failed to find usable security protocols.")
        _usedSec = _goodSecs[0]
        client.log("Using securty %r" % _usedSec.name)
        client.sendData(client.types.pack(("U8", ), (_usedSec.id, )))
        return _usedSec()

class AuthCompleted(StructExpectState):

    struct = ("U32", )

    def act(self, client):
        _data = client.recvData(self.getDataSize(client))
        (_failed, ) = client.types.unpack(self.struct, _data)
        if _failed:
            client.log("Server failed auth with status %i" % _failed)
            _rv = ProtocolFailure()
        else:
            _rv = ClientInitState()
        return _rv

class ClientInitState(BaseState):

    def act(self, client):
        # sending clientInit message
        client.sendData(client.types.pack(
            ("U8", ),
            (1 if client.askSharedSession() else 0, )
        ))
        return ServerInitState()

class ServerInitState(StructExpectState):

    struct = (
        ("framebuffer-width", "U16"),
        ("framebuffer-height", "U16"),
        ("server-pixel-format", "PIXEL_FORMAT"),
    )

    def act(self, client):
        _data = client.recvData(self.getDataSize(client))
        _mode = client.types.unpackDict(self.struct, _data)
        client.onFbDimensionsSet(
            _mode["framebuffer-width"],
            _mode["framebuffer-height"]
        )
        client.initialized = True
        client.setActivePixelFormat(_mode["server-pixel-format"])
        _clientIdea = client.getPixelFormat()
        if _clientIdea:
            client.setPixelFormat(_clientIdea)
        return GetNameState()

class GetNameState(ReceiveStringState):

    def act(self, client):
        _name = client.recvData(self.getDataSize(client))
        client.nameSet(_name)
        encodings.setEncodings(client)
        # initialization finished.
        # Proceeding to core state
        return coreStates.getMainState()

def getStartState():
    """Return initialization sequence first state."""
    return GetServerVersion()

# vim: set sts=4 sw=4 et :
